import type { GlobalThemeOverrides } from 'naive-ui';
import { addColorAlpha, getColorPalette, getPaletteColorByNumber, getRgb } from '@sa/color';
import { overrideThemeSettings, themeSettings } from '@/theme/settings';
import { themeVars } from '@/theme/vars';
import { toggleHtmlClass } from '@/utils/common';
import { localStg } from '@/utils/storage';

const DARK_CLASS = 'dark';

/** Init theme settings */
export function initThemeSettings() {
    const isProd = import.meta.env.PROD;

    // if it is development mode, the theme settings will not be cached, by update `themeSettings` in `src/theme/settings.ts` to update theme settings
    if (!isProd) return themeSettings;

    // if it is production mode, the theme settings will be cached in localStorage
    // if want to update theme settings when publish new version, please update `overrideThemeSettings` in `src/theme/settings.ts`

    const settings = localStg.get('themeSettings') || themeSettings;

    const isOverride = localStg.get('overrideThemeFlag') === BUILD_TIME;

    if (!isOverride) {
        Object.assign(settings, overrideThemeSettings);
        localStg.set('overrideThemeFlag', BUILD_TIME);
    }

    return settings;
}

/**
 * create theme token css vars value by theme settings
 *
 * @param colors Theme colors
 * @param tokens Theme setting tokens
 * @param [recommended=false] Use recommended color. Default is `false`
 */
export function createThemeToken(
    colors: App.Theme.ThemeColor,
    tokens?: App.Theme.ThemeSetting['tokens'],
    recommended = false
) {
    const paletteColors = createThemePaletteColors(colors, recommended);

    const { light, dark } = tokens || themeSettings.tokens;

    const themeTokens: App.Theme.ThemeTokenCSSVars = {
        colors: {
            ...paletteColors,
            nprogress: paletteColors.primary,
            ...light.colors
        },
        boxShadow: {
            ...light.boxShadow
        }
    };

    const darkThemeTokens: App.Theme.ThemeTokenCSSVars = {
        colors: {
            ...themeTokens.colors,
            ...dark?.colors
        },
        boxShadow: {
            ...themeTokens.boxShadow,
            ...dark?.boxShadow
        }
    };

    return {
        themeTokens,
        darkThemeTokens
    };
}

/**
 * Create theme palette colors
 *
 * @param colors Theme colors
 * @param [recommended=false] Use recommended color. Default is `false`
 */
function createThemePaletteColors(colors: App.Theme.ThemeColor, recommended = false) {
    const colorKeys = Object.keys(colors) as App.Theme.ThemeColorKey[];
    const colorPaletteVar = {} as App.Theme.ThemePaletteColor;

    colorKeys.forEach((key) => {
        const colorMap = getColorPalette(colors[key], recommended);

        colorPaletteVar[key] = colorMap.get(500)!;

        colorMap.forEach((hex, number) => {
            colorPaletteVar[`${key}-${number}`] = hex;
        });
    });

    return colorPaletteVar;
}

/**
 * Get css var by tokens
 *
 * @param tokens Theme base tokens
 */
function getCssVarByTokens(tokens: App.Theme.BaseToken) {
    const styles: string[] = [];

    function removeVarPrefix(value: string) {
        return value.replace('var(', '').replace(')', '');
    }

    function removeRgbPrefix(value: string) {
        return value.replace('rgb(', '').replace(')', '');
    }

    for (const [key, tokenValues] of Object.entries(themeVars)) {
        for (const [tokenKey, tokenValue] of Object.entries(tokenValues)) {
            let cssVarsKey = removeVarPrefix(tokenValue);
            let cssValue = tokens[key][tokenKey];

            if (key === 'colors') {
                cssVarsKey = removeRgbPrefix(cssVarsKey);
                const { r, g, b } = getRgb(cssValue);
                cssValue = `${r} ${g} ${b}`;
            }

            styles.push(`${cssVarsKey}: ${cssValue}`);
        }
    }

    const styleStr = styles.join(';');

    return styleStr;
}

/**
 * Add theme vars to global
 *
 * @param tokens
 */
export function addThemeVarsToGlobal(tokens: App.Theme.BaseToken, darkTokens: App.Theme.BaseToken) {
    const cssVarStr = getCssVarByTokens(tokens);
    const darkCssVarStr = getCssVarByTokens(darkTokens);

    const css = `
    :root {
      ${cssVarStr}
    }
  `;

    const darkCss = `
    html.${DARK_CLASS} {
      ${darkCssVarStr}
    }
  `;

    const styleId = 'theme-vars';

    const style = document.querySelector(`#${styleId}`) || document.createElement('style');

    style.id = styleId;

    style.textContent = css + darkCss;

    document.head.appendChild(style);
}

/**
 * Toggle css dark mode
 *
 * @param darkMode Is dark mode
 */
export function toggleCssDarkMode(darkMode = false) {
    const { add, remove } = toggleHtmlClass(DARK_CLASS);

    if (darkMode) {
        add();
    } else {
        remove();
    }
}

/**
 * Toggle auxiliary color modes
 *
 * @param grayscaleMode
 * @param colourWeakness
 */
export function toggleAuxiliaryColorModes(grayscaleMode = false, colourWeakness = false) {
    const htmlElement = document.documentElement;
    htmlElement.style.filter = [
        grayscaleMode ? 'grayscale(100%)' : '',
        colourWeakness ? 'invert(80%)' : ''
    ]
        .filter(Boolean)
        .join(' ');
}

type NaiveColorScene = '' | 'Suppl' | 'Hover' | 'Pressed' | 'Active';
type NaiveColorKey = `${App.Theme.ThemeColorKey}Color${NaiveColorScene}`;
type NaiveThemeColor = Partial<Record<NaiveColorKey, string>>;
interface NaiveColorAction {
    scene: NaiveColorScene;
    handler: (color: string) => string;
}

/**
 * Get naive theme colors
 *
 * @param colors Theme colors
 * @param [recommended=false] Use recommended color. Default is `false`
 */
function getNaiveThemeColors(colors: App.Theme.ThemeColor, recommended = false) {
    const colorActions: NaiveColorAction[] = [
        { scene: '', handler: (color) => color },
        { scene: 'Suppl', handler: (color) => color },
        { scene: 'Hover', handler: (color) => getPaletteColorByNumber(color, 500, recommended) },
        { scene: 'Pressed', handler: (color) => getPaletteColorByNumber(color, 700, recommended) },
        { scene: 'Active', handler: (color) => addColorAlpha(color, 0.1) }
    ];

    const themeColors: NaiveThemeColor = {};

    const colorEntries = Object.entries(colors) as [App.Theme.ThemeColorKey, string][];

    colorEntries.forEach((color) => {
        colorActions.forEach((action) => {
            const [colorType, colorValue] = color;
            const colorKey: NaiveColorKey = `${colorType}Color${action.scene}`;
            themeColors[colorKey] = action.handler(colorValue);
        });
    });

    return themeColors;
}

/**
 * Get naive theme
 *
 * @param colors Theme colors
 * @param [recommended=false] Use recommended color. Default is `false`
 */
export function getNaiveTheme(colors: App.Theme.ThemeColor, recommended = false) {
    const { primary: colorLoading } = colors;

    const theme: GlobalThemeOverrides = {
        common: {
            ...getNaiveThemeColors(colors, recommended),
            borderRadius: '6px'
        },
        LoadingBar: {
            colorLoading
        },
        Tag: {
            borderRadius: '6px'
        }
    };

    return theme;
}
